package com.example.springbootmbyatisautogenerate.plugin;

import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.PreparedStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Objects;
import java.util.Properties;

/**
 * 工具类，用来分页。
 * 这个类是在所有的条件都准备好了之后来进行赋值操作的！
 *
 * @author liguang
 * @date 2022/9/5 9:28
 */
@Intercepts({@Signature(
        type = StatementHandler.class,
        method = "prepare",
        args = {Connection.class, Integer.class})})
public class PagePlugin implements Interceptor {

    private static final String PAGE_CONDITION_SQL = "SELECT COUNT(0) FROM ( %s ) AS _PAGE";

    /**
     * 重点拦截方法
     *
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 被代理的对象StatementHandler
        // 从这里可以获取得到一切想要的值来进行操作
        Object target = invocation.getTarget();
        Object delegate = SystemMetaObject.forObject(target).getValue("delegate");
        // 1、判断是否满足分页查询条件
        if (delegate instanceof PreparedStatementHandler) {
            PreparedStatementHandler preparedStatementHandler = (PreparedStatementHandler) delegate;
            MetaObject metaObject = SystemMetaObject.forObject(preparedStatementHandler);
            MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("mappedStatement");
            if (SqlCommandType.SELECT == mappedStatement.getSqlCommandType()) {
                // 使用线程上下文来进行判断使用而已
                PageInfo pageInfo = PageUtil.getPageInfo();
                if (Objects.isNull(pageInfo)) {
                    return invocation.proceed();
                }
                executeCountSQL(preparedStatementHandler, invocation);
                executePageSQL(preparedStatementHandler, invocation);
                PageUtil.removePageInfo();
            }
        }
        return invocation.proceed();
    }

    private boolean validateSqlCommandType(MappedStatement mappedStatement) {
        if (SqlCommandType.SELECT != mappedStatement.getSqlCommandType()) {
            throw new IllegalArgumentException("SQL使用错误！请使用SELECT标签操作");
        }
        return true;
    }

    private void executePageSQL(PreparedStatementHandler preparedStatementHandler, Invocation invocation) throws InvocationTargetException, IllegalAccessException {
        BoundSql boundSql = preparedStatementHandler.getBoundSql();
        String originalSql = boundSql.getSql();
        PageInfo pageInfo = PageUtil.getPageInfo();
        String pageSql = String.format("%s LIMIT %s , %s", originalSql, pageInfo.getStartRowNum(), pageInfo.getEndRowNum());
        SystemMetaObject.forObject(boundSql).setValue("sql", pageSql);
    }

    /**
     * 查询分页之后的总条数
     *
     * @param invocation
     * @return
     */
    private int executeCountSQL(PreparedStatementHandler preparedStatementHandler, Invocation invocation) throws SQLException {
        String countSql = createCountSql(preparedStatementHandler);
        int count = 0;
        Object[] args = invocation.getArgs();
        Connection connection = (Connection) args[0];
        PreparedStatement preparedStatement = connection.prepareStatement(countSql);
        preparedStatementHandler.parameterize(preparedStatement);
        ResultSet resultSet = preparedStatement.executeQuery();
        while (resultSet.next()){
            count = resultSet.getInt(1);
        }
        resultSet.close();
        preparedStatement.close();
        return count;
    }

    private String createCountSql(PreparedStatementHandler preparedStatementHandler) {
        BoundSql boundSql = preparedStatementHandler.getBoundSql();
        String originalSql = boundSql.getSql();
        String countSql = String.format(PAGE_CONDITION_SQL, originalSql);
        return countSql;
    }


    /**
     * 获取得到插件的代理对象
     * 可以不需要管理
     *
     * @param target
     * @return
     */
    @Override
    public Object plugin(Object target) {
        return Interceptor.super.plugin(target);
    }

    /**
     * 获取获取得到参数
     * 可以在这里获取得到分页拦截到的值！
     * 可以来进行自定义操作信息！也可以传递到配置文件中来！所以这也为mybatis-plus提供了一个遍历的地方！
     * @param properties
     */
    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(properties);
    }
}
